package com.lagou.edu.factory;

import com.lagou.edu.annot.Autowired;
import com.lagou.edu.annot.Bean;
import com.lagou.edu.annot.Service;
import com.lagou.edu.annot.Transactional;
import com.lagou.edu.service.TransferService;
import com.lagou.edu.utils.ClassUtil;

import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 注解创建bean工厂
 */
public class AnontBeanFactory {
    private Map<String,Object> map = new ConcurrentHashMap<>();  // 存储对象 一级
    //处理依赖属性创建
    private Map<String,Object> tempMap = new ConcurrentHashMap<>();

    //存储接口类与实现类, 原则上需要 类上存在注解（接口、或实现类都行）
    private Map<String,String> classMap = new ConcurrentHashMap<>();

    public  AnontBeanFactory(String path) throws InstantiationException, IllegalAccessException, InvocationTargetException {
        //获取所有类
        List<Class<?>> list =  ClassUtil.getClasses(path);

        //扫描 将所有有注解的类、且有父级接口的类存入 classMap中
        // key 是类的全限定名 value 是 beanName
        for (Class<?> aClass : list) {
            if(aClass.isAnnotationPresent(Service.class)
                    || aClass.isAnnotationPresent(Bean.class) ) {
                doParseClass(aClass);
            }
        }

        for (Class<?> aClass : list) {
            if(aClass.isAnnotationPresent(Service.class)
                || aClass.isAnnotationPresent(Bean.class) ) {
                String beanName = null;
                Service annotation = aClass.getAnnotation(Service.class);
                if(annotation!=null && !"".equals(annotation.value()) ){
                    beanName = annotation.value();
                }
                Bean annotBean = aClass.getAnnotation(Bean.class);
                if(annotBean!=null && !"".equals(annotBean.value()) ){
                    beanName = annotBean.value();
                }

                doParseServiceAnont(aClass,beanName);
            }


            //获取所有方法
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Transactional.class)) {
                    doParseTransactionalAnont(aClass,method);

                }
            }

        }

    }



    /**
     * 解析Transactional注解,给方法添加动态代理, 然后替换缓存中的对象为代理对象
     * @param method
     */
    private void doParseTransactionalAnont(Class<?> aClass,Method method) throws InvocationTargetException, IllegalAccessException {
        String beanName = getTypeName(aClass);
        Object classObj = map.get(beanName);

        ProxyFactory pf = (ProxyFactory)map.get(getTypeName(ProxyFactory.class));
        Object jdkProxy = pf.getJdkProxy(classObj);
        map.put(beanName, jdkProxy);
    }

    /**
     * 解析autowired注解 注入对象
     * @param field
     */
    private void doParseAutoWiredAnont(Class<?> aClass,Field field) throws InstantiationException, IllegalAccessException {
        String beanName = getTypeName(aClass);
        Object classObj = tempMap.get(beanName);
        Class<?> classType = field.getType();

        field.setAccessible(true);
        //获取要注入的字段对象，如果无值则创建
        Object fieldValueObj = doParseServiceAnont(classType,null);
        field.set(classObj,fieldValueObj);
    }

    /**
     * 解析service注解 创建bean
     * @param aClass
     */
    private Object doParseServiceAnont(Class<?> aClass,String beanName) throws IllegalAccessException, InstantiationException {
        if(beanName==null)
            beanName = getTypeName(aClass);

        //从缓存中获取对象
        Object o = getBeanByClassName(beanName);
        if (o == null) {
            o=aClass.newInstance();
            //添加临时缓存
            tempMap.put(beanName,o);

            //是否存在需要注入的属性
            //获取所有字段
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    doParseAutoWiredAnont(aClass,field);
                }
            }
            //对象创建成功，转换到一级缓存
            addCache(beanName);
        }
        return o;
    }

    /**
     * 通过全限定名获取beanName
     *   接口情况下，以实现类的类名为beanName（首字母小写）
     * @param aClass
     * @return
     */
    private String getTypeName(Class<?> aClass) {

        //判断是否是接口, 则循环获取实现的子类
        if(aClass.isInterface()){
            Object o = classMap.get(aClass.getCanonicalName());
            if(o==null){
                System.out.println(aClass.getCanonicalName());
                return null;
            }else{
                return o.toString();
            }
        }else {
            return getBeanNameByClass(aClass);
        }
    }

    /**
     * 扫描需要注入的实现类、接口类，方便双向查找
     * 暂 不支持一个接口多个实现情况
     * @param aClass
     */
    private void doParseClass(Class<?> aClass) {
        String beanName = getBeanNameByClass(aClass);
        classMap.put(aClass.getCanonicalName(), beanName);
        Class<?>[] interfaces = aClass.getInterfaces();
        for (Class<?> interfaceClass : interfaces) {
            if(interfaceClass.getCanonicalName().indexOf("java.lang.Object")==-1){
                classMap.put(interfaceClass.getCanonicalName(), beanName);
            }
        }
    }

    private String getBeanNameByClass(Class<?> aClass){
        String canonicalName = aClass.getCanonicalName();
        String name = canonicalName.substring(canonicalName.lastIndexOf(".")+1, canonicalName.length());
        return toLowerCaseFirstOne(name);
    }

    //首字母转小写
    public static String toLowerCaseFirstOne(String s){
        if(Character.isLowerCase(s.charAt(0)))
            return s;
        else
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }


    /**
     * 将对象转换到1级缓存
     * @param beanName
     */
    private void addCache(String beanName) {
        Object t = tempMap.get(beanName);
        if(t!=null){
            map.put(beanName,t);
            tempMap.remove(beanName);
        }
    }

    /**
     * 从1 2级缓存中获取对象
     * @param canonicalName
     * @return
     */
    private Object getBeanByClassName(String canonicalName) {
        Object t = map.get(canonicalName);
        if(t==null){
            t = tempMap.get(canonicalName);
        }
        return t;
    }


    /**
     * 获取bean
     * @param aClass
     * @return
     */
    public Object getBeanByType(Class<?> aClass) {
        String beanName = getBeanNameByClass(aClass);
        return map.get(beanName);
    }

}
